package com.mdl.common.lock.redsession;

import com.mdl.common.lock.DistributedLockAble;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;

@Aspect
@Order(0)
public class DistributedLockAOP {
    @Autowired
    private DistributedLock distributedLock;

    @Around("@annotation(com.mdl.lock.DistributedLockAble)")
    public Object lockAround(ProceedingJoinPoint pjp) throws Throwable {
        if (distributedLock == null) {
            throw new RuntimeException("distributedLock is null");
        }

        DistributedLockMeta distributedLockMeta = this.getDistributedLockMeta(pjp);

        try {
            boolean result = this.distributedLock.tryLock(distributedLockMeta.getLockName(), distributedLockMeta.getWaiting(), distributedLockMeta.getExpire());
            if (result) {
                return pjp.proceed();
            } else {
                throw new RuntimeException("try lock 【" + distributedLockMeta.getLockName() + "】 fail!");
            }
        } finally {
            this.distributedLock.unlock(distributedLockMeta.getLockName());
        }
    }

    private DistributedLockMeta getDistributedLockMeta(ProceedingJoinPoint pjp) {
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        DistributedLockAble distLockAble = methodSignature.getMethod().getAnnotation(DistributedLockAble.class);
        int argIdx = distLockAble.argIdx();
        Object[] args = pjp.getArgs();
        StringBuilder sb = new StringBuilder();
        sb.append(distLockAble.prefix()).append("-").append(args[argIdx]);

        String lockName = sb.toString();
        int waiting = distLockAble.waiting();
        int expire = distLockAble.expire();

        DistributedLockMeta distributedLockMeta = new DistributedLockMeta();
        distributedLockMeta.setLockName(lockName);
        distributedLockMeta.setWaiting(waiting);
        distributedLockMeta.setExpire(expire);

        return distributedLockMeta;
    }
}
